package com.hgxd.fillter;

import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.hgxd.entity.authentication.CustomUser;
import com.hgxd.entity.res.ResUserVo;
import com.hgxd.entity.vo.LoginVo;
import com.hgxd.result.Result;
import com.hgxd.util.ResponseUtil;
import com.hgxd.utils.security.JwtHelper;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @author wtf
 * @create 2023-07-24 17:38
 * @description : 登录过滤器，继承UsernamePasswordAuthenticationFilter，对用户名密码进行登录校验
 */
public class TokenLoginFilter extends UsernamePasswordAuthenticationFilter {

    private StringRedisTemplate stringRedisTemplate;

    public TokenLoginFilter(StringRedisTemplate redisTemplate, AuthenticationManager authenticationManager) {
        this.stringRedisTemplate = redisTemplate;
        super.setAuthenticationManager(authenticationManager);
        //告诉当前Filter，白名单   如果当前的url是去认证的url，那么直接放行
        this.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/api/login", "POST"));
    }

    /**
     * 尝试认证
     */
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        try {
            //1.从请求体中获取认证信息（其实就是用户名和密码）
            LoginVo loginVo = new ObjectMapper().readValue(request.getInputStream(), LoginVo.class);
            //2.封装成一个待认证的对象
            Authentication authentication = new UsernamePasswordAuthenticationToken(loginVo.getUsername(), loginVo.getPassword());
            //3.通过认证管理器去做认证
            return getAuthenticationManager().authenticate(authentication);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }


    /**
     * 认证成功之后
     *
     * @param request
     * @param response
     * @param chain
     * @param authentication
     * @throws IOException
     * @throws ServletException
     */
    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authentication) throws IOException, ServletException {
        //1.从认证成功之后的认证对象中获取个人信息
        CustomUser principal = (CustomUser) authentication.getPrincipal();


        //2.生成token(UUID)
//        String token = UUID.randomUUID().toString().replaceAll("-","");
        //2.根据userId和userName生成token
        String token = JwtHelper.createToken(principal.getSysUser().getUserId(), principal.getSysUser().getUsername());

        //3.将用户信息保存到redis中
        String userMessage = JSON.toJSONString(principal);
        stringRedisTemplate.boundValueOps(token).set(userMessage);

        //4.将token响应给客户端
        Map<String, Object> map = new HashMap<>();

        // 将查询到的对象封装成前端需要的对象
        String jsonString = JSON.toJSONString(principal.getSysUser());
        ResUserVo user = JSON.parseObject(jsonString, ResUserVo.class);

//        map.put("principal", principal);
        map.put("user", user);
        map.put("token", token);


        ResponseUtil.out(response, Result.ok(map));
    }

    //认证失败之后
    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
        ResponseUtil.out(response, Result.build(null, 444, failed.getMessage()));
    }


}
